﻿using EnvDTE;
using EnvDTE80;
using Sasa.QualityTools.CodeAnalysis.Common;
using Sasa.QualityTools.CodeAnalysis.Core.VisualStudio;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;

namespace Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.VisualStudio
{
    public class Solution : ISolution
    {
        private EnvDTE.Solution solution;


        public bool HasFxCopProjectFile
        {
            get
            {
                return File.Exists(FxCopProjectPath);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public string FxCopProjectPath
        {
            get
            {
                try
                {
                    return Path.Combine(ConfigurationDir, Core.Constants.FxCopProjectFileName);
                }
                catch (Exception ex)
                {
                    Logger.Error(ex, "Failed to get FxCop Project path because: ");
                    return null;
                }
            }
        }

        public string ConfigurationDir
        {
            get
            {
                return Path.Combine(Path.GetDirectoryName(solution.FullName), Core.Constants.CodeAnalysisConfigurationDirName);
            }
        }

        public string ConfigurationPath
        {
            get
            {
                return Path.Combine(ConfigurationDir, Core.Constants.CodeAnalysisConfigurationFileName);
            }
        }

        public string FxCopDictionaryPath
        {
            get
            {
                return Path.Combine(ConfigurationDir, Core.Constants.FxCopDictionaryFileName);
            }
        }

        public IProject CurrentProject
        {
            get
            {
                return Project.CreateInstance(solution.DTE.SelectedItems.Item(1).Project);
            }
        }

        public IEnumerable<IProject> SelectedProjects
        {
            get
            {
                var items = solution.DTE.SelectedItems;
                var count = items.Count;
                for (int i = 1; i <=count; i++)
                {
                    yield return Project.CreateInstance(items.Item(i).Project);
                }
            }
        }


        public Solution(EnvDTE.Solution solution)
        {
            this.solution = solution;
        }


        public bool Build()
        {
            solution.SolutionBuild.Build(true);
            return solution.SolutionBuild.LastBuildInfo == 0;
        }

        public void Open(string path, int? line)
        {
            var item = solution.FindProjectItem(path);
            if (item == null) return;

            if (item.IsOpen)
            {
                item.Document.Activate();
            }
            else
            {
                var window = item.Open(EnvDTE.Constants.vsViewKindCode);
                window.Visible = true;
                window.Activate();
            }

            // TODO get line
            if (line.HasValue)
            {
                ((TextSelection)item.Document.Selection).GotoLine(line.Value);
            }
        }

        public IProject AddSolutionFolder(string name)
        {
            foreach (EnvDTE.Project project in solution.Projects)
            {
                if (project.Name == name && project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
                {
                    return Project.CreateInstance(project);
                }
            }

            return Project.CreateInstance(((Solution2)solution).AddSolutionFolder(name));
        }

        public IProject FindProjectByUniqueName(string uniqueName)
        {
            for (int i = 1; i <= solution.Count; i++)
            {
                var project = FindProjectByUniqueName(solution.Item(i), uniqueName);
                if (project != null)
                {
                    return project;
                }
            }
            return null;
        }

        private IProject FindProjectByUniqueName(EnvDTE.Project project, string uniqueName)
        {
            if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
            {
                return FindProjectByUniqueNameFromSolutionFolder(project, uniqueName);
            }
            else if (project.UniqueName == uniqueName)
            {
                return Project.CreateInstance(project);
            }
            else
            {
                return null;
            }
        }

        private IProject FindProjectByUniqueNameFromSolutionFolder(EnvDTE.Project solutionFolder, string uniqueName)
        {
            for (int i = 1; i <= solutionFolder.ProjectItems.Count; i++)
            {
                var project = solutionFolder.ProjectItems.Item(i).Object as EnvDTE.Project;
                if (project != null)
                {
                    var result = FindProjectByUniqueName(project, uniqueName);
                    if (result != null)
                    {
                        return result;
                    }
                }
            }
            return null;
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public IEnumerable<IProject> GetBuildTargetProjects(bool onlyShouldBuildProject)
        {
            var projects = new List<IProject>();

            try
            {
                var dependencies = GetBuildDepnedencies();
                var contexts = GetSolutionContexts();
                foreach (var dependency in dependencies)
                {
                    if (!onlyShouldBuildProject)
                    {
                        projects.Add(Project.CreateInstance(dependency.Project));
                    }
                    else
                    {
                        var context = contexts.FirstOrDefault(o => o.ProjectName == dependency.Project.UniqueName);
                        if (context != null && context.ShouldBuild)
                        {
                            projects.Add(Project.CreateInstance(dependency.Project));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Failed to get build target projects because: ");
            }

            return projects;
        }

        private IEnumerable<BuildDependency> GetBuildDepnedencies()
        {
            var result = new List<BuildDependency>();

            var dependencies = solution.SolutionBuild.BuildDependencies;
            var count = dependencies.Count;
            Logger.Info("The nubmer of build dependencies is {0}.", count);

            for (var i = 1; i <= count; i++)
            {
                try
                {
                    result.Add(dependencies.Item(i));
                }
                catch (ArgumentException ex)
                {
                    Logger.Error(ex, "Ignored build dependency at index {0}.", i);
                }
            }

            Logger.Info("Build dependencies are {0}",
                String.Join(",", result.Select(o => String.Format(CultureInfo.CurrentUICulture, "[{0}:{1}]", o.Project.UniqueName, o.Project.Kind))));

            return result;
        }

        private IEnumerable<SolutionContext> GetSolutionContexts()
        {
            var result = new List<SolutionContext>();

            var contexts = solution.SolutionBuild.ActiveConfiguration.SolutionContexts;
            var count = contexts.Count;
            Logger.Info("The number of solution contexts is {0}.", count);

            for (var i = 1; i <= count; i++)
            {
                try
                {
                    result.Add(contexts.Item(i));
                }
                catch (ArgumentException ex)
                {
                    Logger.Error(ex, "Ignored solution context at index {0}.", i);
                }
            }

            Logger.Info("Solution contexts are {0}",
                String.Join(",", result.Select(o => String.Format(CultureInfo.CurrentUICulture, "[{0}:{1}:{2}:{3}]", o.ProjectName, o.PlatformName, o.ConfigurationName, o.ShouldBuild))));

            return result;
        }
    }
}
